import nodemailer, { Transporter, SendMailOptions } from 'nodemailer';
import { createClient, SupabaseClient } from '@supabase/supabase-js';
import winston from 'winston';
import { v4 as uuidv4 } from 'uuid';
import { EmailTemplate } from './EmailTemplateService';
import { EmailTracker } from './EmailTrackingService';
import { PersonalizationEngine } from './PersonalizationEngine';

export interface EmailDeliveryOptions {
  to: string;
  subject: string;
  html: string;
  text: string;
  userId: string;
  digestId?: string;
  templateType: string;
  metadata?: Record<string, any>;
}

export interface EmailDeliveryResult {
  success: boolean;
  messageId?: string;
  error?: string;
  deliveryLogId: string;
}

export interface SMTPConfig {
  host: string;
  port: number;
  secure: boolean;
  auth: {
    user: string;
    pass: string;
  };
  pool: boolean;
  maxConnections: number;
  maxMessages: number;
  rateDelta: number;
  rateLimit: number;
}

export class EmailService {
  private transporter: Transporter;
  private supabase: SupabaseClient;
  private logger: winston.Logger;
  private templateService: EmailTemplate;
  private trackingService: EmailTracker;
  private personalizationEngine: PersonalizationEngine;
  private config: SMTPConfig;
  private deliveryQueue: Map<string, EmailDeliveryOptions[]> = new Map();
  private isProcessingQueue = false;

  constructor(
    smtpConfig: SMTPConfig,
    supabaseUrl: string,
    supabaseKey: string,
    logger: winston.Logger
  ) {
    this.config = smtpConfig;
    this.logger = logger;
    this.supabase = createClient(supabaseUrl, supabaseKey);
    
    // Initialize SMTP transporter with connection pooling
    this.transporter = nodemailer.createTransporter({
      host: this.config.host,
      port: this.config.port,
      secure: this.config.secure,
      auth: this.config.auth,
      pool: this.config.pool,
      maxConnections: this.config.maxConnections,
      maxMessages: this.config.maxMessages,
      rateDelta: this.config.rateDelta,
      rateLimit: this.config.rateLimit,
      // Enhanced delivery settings
      connectionTimeout: 60000,
      greetingTimeout: 30000,
      socketTimeout: 60000,
      tls: {
        rejectUnauthorized: false
      }
    });

    this.templateService = new EmailTemplate(supabaseUrl, supabaseKey, logger);
    this.trackingService = new EmailTracker(supabaseUrl, supabaseKey, logger);
    this.personalizationEngine = new PersonalizationEngine(supabaseUrl, supabaseKey, logger);

    // Verify SMTP connection on startup
    this.verifyConnection();
    
    // Start queue processor
    this.startQueueProcessor();
  }

  /**
   * Verify SMTP connection
   */
  private async verifyConnection(): Promise<void> {
    try {
      await this.transporter.verify();
      this.logger.info('SMTP connection verified successfully');
    } catch (error) {
      this.logger.error('SMTP connection failed:', error);
      throw new Error('Failed to connect to SMTP server');
    }
  }

  /**
   * Send personalized email to user
   */
  async sendPersonalizedEmail(
    userId: string,
    templateType: string,
    templateData: Record<string, any> = {},
    options: Partial<EmailDeliveryOptions> = {}
  ): Promise<EmailDeliveryResult> {
    try {
      // Get user information
      const { data: user, error: userError } = await this.supabase
        .from('users')
        .select('email, first_name, last_name, timezone, is_active')
        .eq('user_id', userId)
        .eq('is_active', true)
        .single();

      if (userError || !user) {
        throw new Error(`User not found or inactive: ${userId}`);
      }

      // Check if user has email notifications enabled
      const { data: preferences } = await this.supabase
        .from('notification_preferences')
        .select('is_enabled')
        .eq('user_id', userId)
        .eq('notification_type', 'email_digest')
        .eq('delivery_method', 'email')
        .single();

      if (preferences && !preferences.is_enabled) {
        this.logger.info(`Email notifications disabled for user: ${userId}`);
        return {
          success: false,
          error: 'Email notifications disabled',
          deliveryLogId: uuidv4()
        };
      }

      // Generate personalized content
      const personalizedData = await this.personalizationEngine.personalizeContent(
        userId,
        templateType,
        templateData
      );

      // Generate email from template
      const emailContent = await this.templateService.generateEmail(
        templateType,
        {
          ...personalizedData,
          user: {
            firstName: user.first_name,
            lastName: user.last_name,
            email: user.email
          }
        }
      );

      // Add tracking pixels and links
      const trackedContent = await this.trackingService.addTracking(
        emailContent.html,
        userId,
        options.digestId || uuidv4(),
        templateType
      );

      // Prepare email options
      const emailOptions: EmailDeliveryOptions = {
        to: user.email,
        subject: emailContent.subject,
        html: trackedContent.html,
        text: emailContent.text,
        userId,
        templateType,
        ...options
      };

      // Send email
      return await this.deliverEmail(emailOptions);

    } catch (error) {
      this.logger.error('Failed to send personalized email:', error);
      return {
        success: false,
        error: error instanceof Error ? error.message : 'Unknown error',
        deliveryLogId: uuidv4()
      };
    }
  }

  /**
   * Send bulk emails with rate limiting
   */
  async sendBulkEmails(
    emails: Array<{
      userId: string;
      templateType: string;
      templateData?: Record<string, any>;
      options?: Partial<EmailDeliveryOptions>;
    }>,
    batchSize: number = 100,
    delayBetweenBatches: number = 1000
  ): Promise<EmailDeliveryResult[]> {
    const results: EmailDeliveryResult[] = [];
    
    this.logger.info(`Starting bulk email delivery for ${emails.length} emails`);

    // Process emails in batches
    for (let i = 0; i < emails.length; i += batchSize) {
      const batch = emails.slice(i, i + batchSize);
      
      // Add batch to queue
      const batchId = uuidv4();
      const batchEmails = await Promise.all(
        batch.map(async (email) => {
          try {
            // Pre-generate personalized content
            const user = await this.getUserInfo(email.userId);
            if (!user || !user.is_active) {
              results.push({
                success: false,
                error: 'User not found or inactive',
                deliveryLogId: uuidv4()
              });
              return null;
            }

            const personalizedData = await this.personalizationEngine.personalizeContent(
              email.userId,
              email.templateType,
              email.templateData || {}
            );

            const emailContent = await this.templateService.generateEmail(
              email.templateType,
              {
                ...personalizedData,
                user: {
                  firstName: user.first_name,
                  lastName: user.last_name,
                  email: user.email
                }
              }
            );

            const trackedContent = await this.trackingService.addTracking(
              emailContent.html,
              email.userId,
              email.options?.digestId || uuidv4(),
              email.templateType
            );

            return {
              to: user.email,
              subject: emailContent.subject,
              html: trackedContent.html,
              text: emailContent.text,
              userId: email.userId,
              templateType: email.templateType,
              ...email.options
            } as EmailDeliveryOptions;

          } catch (error) {
            this.logger.error(`Failed to prepare email for user ${email.userId}:`, error);
            results.push({
              success: false,
              error: error instanceof Error ? error.message : 'Unknown error',
              deliveryLogId: uuidv4()
            });
            return null;
          }
        })
      );

      const validEmails = batchEmails.filter(email => email !== null) as EmailDeliveryOptions[];
      
      if (validEmails.length > 0) {
        this.addToQueue(batchId, validEmails);
      }

      // Add delay between batches to respect rate limits
      if (i + batchSize < emails.length) {
        await this.delay(delayBetweenBatches);
      }
    }

    // Wait for queue to process
    await this.waitForQueueCompletion();

    this.logger.info(`Bulk email delivery completed. Success: ${results.filter(r => r.success).length}, Failed: ${results.filter(r => !r.success).length}`);
    
    return results;
  }

  /**
   * Add emails to delivery queue
   */
  private addToQueue(batchId: string, emails: EmailDeliveryOptions[]): void {
    if (!this.deliveryQueue.has(batchId)) {
      this.deliveryQueue.set(batchId, []);
    }
    this.deliveryQueue.get(batchId)!.push(...emails);
  }

  /**
   * Process email delivery queue
   */
  private async startQueueProcessor(): Promise<void> {
    if (this.isProcessingQueue) return;

    this.isProcessingQueue = true;

    const processQueue = async () => {
      try {
        for (const [batchId, emails] of this.deliveryQueue.entries()) {
          if (emails.length === 0) {
            this.deliveryQueue.delete(batchId);
            continue;
          }

          // Process one email at a time to respect rate limits
          const email = emails.shift()!;
          try {
            await this.deliverEmail(email);
          } catch (error) {
            this.logger.error(`Failed to deliver email in queue:`, error);
          }

          // Remove empty batches
          if (emails.length === 0) {
            this.deliveryQueue.delete(batchId);
          }

          // Rate limiting delay
          await this.delay(100);
        }
      } catch (error) {
        this.logger.error('Queue processor error:', error);
      }

      // Continue processing
      setTimeout(processQueue, 1000);
    };

    processQueue();
  }

  /**
   * Deliver individual email
   */
  private async deliverEmail(options: EmailDeliveryOptions): Promise<EmailDeliveryResult> {
    const deliveryLogId = uuidv4();

    try {
      // Log delivery attempt
      await this.logDeliveryAttempt(deliveryLogId, options, 'queued');

      const mailOptions: SendMailOptions = {
        from: {
          name: 'ArXiv Research Hub',
          address: this.config.auth.user
        },
        to: options.to,
        subject: options.subject,
        html: options.html,
        text: options.text,
        headers: {
          'X-User-ID': options.userId,
          'X-Template-Type': options.templateType,
          'X-Delivery-ID': deliveryLogId,
          'List-Unsubscribe': `<https://arxiv-hub.com/unsubscribe?token=${await this.generateUnsubscribeToken(options.userId)}>`,
          'List-Unsubscribe-Post': 'List-Unsubscribe=One-Click'
        }
      };

      const result = await this.transporter.sendMail(mailOptions);

      // Log successful delivery
      await this.logDeliveryAttempt(deliveryLogId, options, 'sent', result.messageId);

      this.logger.info(`Email delivered successfully: ${result.messageId} to ${options.to}`);

      return {
        success: true,
        messageId: result.messageId,
        deliveryLogId
      };

    } catch (error) {
      // Log failed delivery
      await this.logDeliveryAttempt(
        deliveryLogId, 
        options, 
        'failed', 
        undefined, 
        error instanceof Error ? error.message : 'Unknown error'
      );

      this.logger.error(`Email delivery failed to ${options.to}:`, error);

      return {
        success: false,
        error: error instanceof Error ? error.message : 'Unknown error',
        deliveryLogId
      };
    }
  }

  /**
   * Log delivery attempt to database
   */
  private async logDeliveryAttempt(
    logId: string,
    options: EmailDeliveryOptions,
    status: string,
    messageId?: string,
    errorMessage?: string
  ): Promise<void> {
    try {
      const { error } = await this.supabase
        .from('email_delivery_logs')
        .insert({
          log_id: logId,
          digest_id: options.digestId,
          user_id: options.userId,
          email_address: options.to,
          delivery_status: status,
          provider_message_id: messageId,
          provider_response: messageId ? { messageId } : null,
          bounce_reason: errorMessage,
          retry_count: 0
        });

      if (error) {
        this.logger.error('Failed to log delivery attempt:', error);
      }
    } catch (error) {
      this.logger.error('Database logging error:', error);
    }
  }

  /**
   * Generate unsubscribe token
   */
  private async generateUnsubscribeToken(userId: string): Promise<string> {
    // This would typically use JWT or similar secure token generation
    return Buffer.from(`${userId}:${Date.now()}`).toString('base64');
  }

  /**
   * Get user information
   */
  private async getUserInfo(userId: string): Promise<any> {
    const { data, error } = await this.supabase
      .from('users')
      .select('email, first_name, last_name, timezone, is_active')
      .eq('user_id', userId)
      .single();

    if (error) {
      throw new Error(`Failed to get user info: ${error.message}`);
    }

    return data;
  }

  /**
   * Wait for queue to complete processing
   */
  private async waitForQueueCompletion(): Promise<void> {
    return new Promise((resolve) => {
      const checkQueue = () => {
        if (this.deliveryQueue.size === 0) {
          resolve();
        } else {
          setTimeout(checkQueue, 1000);
        }
      };
      checkQueue();
    });
  }

  /**
   * Utility delay function
   */
  private delay(ms: number): Promise<void> {
    return new Promise(resolve => setTimeout(resolve, ms));
  }

  /**
   * Get email delivery statistics
   */
  async getDeliveryStats(
    startDate: Date,
    endDate: Date,
    userId?: string
  ): Promise<{
    total: number;
    delivered: number;
    opened: number;
    clicked: number;
    bounced: number;
    failed: number;
  }> {
    try {
      let query = this.supabase
        .from('email_delivery_logs')
        .select('delivery_status')
        .gte('delivery_timestamp', startDate.toISOString())
        .lte('delivery_timestamp', endDate.toISOString());

      if (userId) {
        query = query.eq('user_id', userId);
      }

      const { data, error } = await query;

      if (error) {
        throw error;
      }

      const stats = {
        total: data.length,
        delivered: 0,
        opened: 0,
        clicked: 0,
        bounced: 0,
        failed: 0
      };

      data.forEach(log => {
        switch (log.delivery_status) {
          case 'delivered':
            stats.delivered++;
            break;
          case 'opened':
            stats.opened++;
            stats.delivered++;
            break;
          case 'clicked':
            stats.clicked++;
            stats.opened++;
            stats.delivered++;
            break;
          case 'bounced':
            stats.bounced++;
            break;
          case 'failed':
            stats.failed++;
            break;
        }
      });

      return stats;
    } catch (error) {
      this.logger.error('Failed to get delivery stats:', error);
      throw error;
    }
  }

  /**
   * Close email service and cleanup resources
   */
  async close(): Promise<void> {
    this.isProcessingQueue = false;
    this.transporter.close();
    this.logger.info('Email service closed');
  }
}